package com.wf.ew.common.utils.wechart;

import net.sf.json.JSONObject;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.ConnectException;
import java.net.URL;
import java.security.Key;
import java.security.MessageDigest;
import java.security.Security;
import java.text.MessageFormat;
import java.util.*;

public class WeChatPay {
	public static final String APPID = "wx4eeef13a351f2e79"; // 车队3.0小程序服务号APPID
	public static final String AppSecret = "0dbc7e1d27168fba49fd47e32b89449c"; // 车队3.0小程序服务号AppSecret
	public static final String MCH_ID = "1509766451"; // 支付商户号
	private static final String KEY_ALGORITHM = "AES";
	private static final String ALGORITHM_STR = "AES/CBC/PKCS7Padding";
	private static Key key;
	private static Cipher cipher;

	/**
	 * 根据用户授权code获取access_token
	 */
	public Map<String, String> getAccess_tokenByCode(String code) {
		Map<String, String> data = new HashMap<String, String>();
		String requestUrlMessageFormat = "https://api.weixin.qq.com/sns/jscode2session?appid={0}&secret={1}&js_code={2}&grant_type=authorization_code";
		String requestUrl = MessageFormat.format(requestUrlMessageFormat, APPID, AppSecret, code);
		System.out.println(requestUrl);
		String requestMethod = "GET";
		String outputStr = "";
		JSONObject json = httpRequest(requestUrl, requestMethod, outputStr);
		String access_token = (String) json.get("access_token");
		String openid = (String) json.get("openid");
		String session_key = (String) json.get("session_key");
		System.out.println("json" + json);
		data.put("access_token", access_token);
		System.out.println(json);
		data.put("session_key", session_key);
		data.put("openid", openid);
		return data;
	}

//	@SuppressWarnings({ "finally", "deprecation", "resource" })
//	public static String getToken() {
//		String turl = String.format("%s?grant_type=client_credential&appid=%s&secret=%s",
//				"https://api.weixin.qq.com/cgi-bin/token", APPID, AppSecret);
//		HttpClient client = new DefaultHttpClient();
//		HttpGet get = new HttpGet(turl);
//		JsonParser jsonparer = new JsonParser();// 初始化解析json格式的对象
//		String result = null;
//		try {
//			HttpResponse res = client.execute(get);
//			String responseContent = null; // 响应内容
//			HttpEntity entity = (HttpEntity) res.getEntity();
//			responseContent = EntityUtils.toString((org.apache.http.HttpEntity) entity, "UTF-8");
//			JsonObject json = jsonparer.parse(responseContent).getAsJsonObject();
//			// 将json字符串转换为json对象
//			if (res.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
//				if (json.get("errcode") != null) {// 错误时微信会返回错误码等信息，{"errcode":40013,"errmsg":"invalid
//													// appid"}
//				} else {// 正常情况下{"access_token":"ACCESS_TOKEN","expires_in":7200}
//					result = json.get("access_token").getAsString();
//				}
//			}
//		} catch (Exception e) {
//			e.printStackTrace();
//		} finally {
//			// 关闭连接 ,释放资源
//			client.getConnectionManager().shutdown();
//			return result;
//		}
//	}

	/**
	 * 发起https请求并获取结果
	 * 
	 * @param requestUrl
	 *            请求地址
	 * @param requestMethod
	 *            请求方式（GET、POST）
	 * @param outputStr
	 *            提交的数据
	 * @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值)
	 */
	public static JSONObject httpRequest(String requestUrl, String requestMethod, String outputStr) {
		JSONObject jsonObject = null;
		StringBuffer buffer = new StringBuffer();
		try {
			// 创建SSLContext对象，并使用我们指定的信任管理器初始化
			TrustManager[] tm = { new MyX509TrustManager() };
			SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
			sslContext.init(null, tm, new java.security.SecureRandom());
			// 从上述SSLContext对象中得到SSLSocketFactory对象
			SSLSocketFactory ssf = sslContext.getSocketFactory();

			URL url = new URL(requestUrl);
			HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection();
			httpUrlConn.setSSLSocketFactory(ssf);

			httpUrlConn.setDoOutput(true);
			httpUrlConn.setDoInput(true);
			httpUrlConn.setUseCaches(false);
			// 设置请求方式（GET/POST）
			httpUrlConn.setRequestMethod(requestMethod);

			if ("GET".equalsIgnoreCase(requestMethod))
				httpUrlConn.connect();

			// 当有数据需要提交时
			if (null != outputStr) {
				OutputStream outputStream = httpUrlConn.getOutputStream();
				// 注意编码格式，防止中文乱码
				outputStream.write(outputStr.getBytes("UTF-8"));
				outputStream.close();
			}

			// 将返回的输入流转换成字符串
			InputStream inputStream = httpUrlConn.getInputStream();
			InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
			BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

			String str = null;
			while ((str = bufferedReader.readLine()) != null) {
				buffer.append(str);
			}
			bufferedReader.close();
			inputStreamReader.close();
			// 释放资源
			inputStream.close();
			inputStream = null;
			httpUrlConn.disconnect();
			jsonObject = JSONObject.fromObject(buffer.toString());
		} catch (ConnectException ce) {
			System.err.println("Weixin server connection timed out.");
		} catch (Exception e) {
			System.err.println("https request error");
		}
		return jsonObject;
	}

	/**
	 * web端扫码支付参数
	 * 
	 * @param request
	 * @param name
	 * @param orderno
	 * @param total_fee
	 * @param product_id
	 * @return
	 * @throws Exception
	 */
	public static String getPrepayId(HttpServletRequest request, String name, String orderno, String total_fee,
			String product_id) throws Exception {
		SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
		parameters.put("appid", APPID);
		parameters.put("mch_id", MCH_ID);
		parameters.put("nonce_str", CreateNoncestr());
		parameters.put("body", name);
		parameters.put("out_trade_no", orderno);// 订单号
		parameters.put("total_fee", total_fee);// 支付金额单位：分
		parameters.put("spbill_create_ip", getIpAddr(request));
		parameters.put("notify_url", "http://www.weixin.qq.com/wxpay/pay.php");
		parameters.put("trade_type", "NATIVE");
		parameters.put("product_id", product_id);
		String sign = createSign("UTF-8", parameters);
		parameters.put("sign", sign);
		String requestXML = getRequestXml(parameters);
		System.out.println(sign);
		// 调用统一下单接口
		String result = httpsRequest("https://api.mch.weixin.qq.com/pay/unifiedorder", "POST", requestXML);
		return result;
	}

	/**
	 * 小程序支付参数
	 * 
	 * @param request
	 * @param name
	 * @param orderno
	 * @param total_fee
	 * @return
	 * @throws Exception
	 */
	public static String getappletsPrepayId(HttpServletRequest request, String name, String orderno, String total_fee,
			String openid) throws Exception {
		SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
		parameters.put("appid", APPID);
		parameters.put("mch_id", MCH_ID);
		parameters.put("nonce_str", CreateNoncestr());
		parameters.put("body", name);
		parameters.put("out_trade_no", orderno);// 订单号
		parameters.put("total_fee", total_fee);// 支付金额单位：分
		parameters.put("spbill_create_ip", getIpAddr(request));
		parameters.put("notify_url", "http://www.weixin.qq.com/wxpay/pay.php");
		parameters.put("trade_type", "JSAPI");
		parameters.put("openid", openid);// openid :需tianxie
		String sign = createSign("UTF-8", parameters);
		parameters.put("sign", sign);
		String requestXML = getRequestXml(parameters);
		// 调用统一下单接口
		String result = httpsRequest("https://api.mch.weixin.qq.com/pay/unifiedorder", "POST", requestXML);
		return result;
	}

	/**
	 * 微信提现 xml数据 签名等
	 * 
	 * @param orderno
	 * @param openid
	 * @param amount
	 * @return
	 */
	public static String withdraw(String orderno, String openid, String amount, HttpServletRequest req) {
		StringBuffer xml = new StringBuffer();
		String data = null;
		try {
			String nonceStr = CreateNoncestr();
			xml.append("</xml>");
			SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
			parameters.put("mch_appid", APPID);
			parameters.put("mchid", MCH_ID);
			parameters.put("nonce_str", nonceStr);
			parameters.put("partner_trade_no", orderno);
			parameters.put("openid", openid);
			parameters.put("check_name", "NO_CHECK");
			parameters.put("amount", amount);
			parameters.put("desc", "提现");
			parameters.put("spbill_create_ip", "60.12.218.237");
			String sign = createSign("UTF-8", parameters);
			parameters.put("sign", sign);
			data = getRequestXml(parameters);
			System.out.println("sign:" + sign);
		} catch (Exception e) {
			System.err.println(e.getMessage());
			return null;
		}
		return data;
	}

	/**
	 * 微信查询订单
	 * 
	 * @param request
	 * @param orderno
	 * @return
	 * @throws Exception
	 */
	public static String getPrepayIdSelect(HttpServletRequest request, String orderno) throws Exception {
		SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
		parameters.put("appid", APPID);
		parameters.put("mch_id", MCH_ID);
		parameters.put("nonce_str", CreateNoncestr());
		parameters.put("out_trade_no", orderno);// 订单号
		String sign = createSign("UTF-8", parameters);
		parameters.put("sign", sign);
		String requestXML = getRequestXml(parameters);
		// 调用统一下单接口
		String result = httpsRequest("https://api.mch.weixin.qq.com/pay/orderquery", "POST", requestXML);
		return result;
	}

	/**
	 * @Author: HONGLINCHEN
	 * @Description:微信退款
	 * @param out_trade_no
	 * @param total_fee
	 * @Date: 2017-9-11 14:35
	 * @return:
	 */
	public static String wxPayRefund(String out_trade_no, String transaction_id, String total_fee) {
		StringBuffer xml = new StringBuffer();
		String data = null;
		try {
			String nonceStr = CreateNoncestr();
			xml.append("</xml>");
			SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
			parameters.put("appid", APPID);
			parameters.put("mch_id", MCH_ID);
			parameters.put("nonce_str", nonceStr);
			parameters.put("out_trade_no", out_trade_no);
			parameters.put("out_refund_no", nonceStr);
			parameters.put("fee_type", "CNY");
			parameters.put("total_fee", total_fee);
			parameters.put("refund_fee", total_fee);
			parameters.put("op_user_id", MCH_ID);
			String sign = createSign("UTF-8", parameters);
			parameters.put("sign", sign);
			data = getRequestXml(parameters);
			System.out.println("data:" + data);
		} catch (Exception e) {
			System.err.println(e.getMessage());
			return null;
		}
		return data;
	}

	public static String CreateNoncestr() {
		String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
		String res = "";
		for (int i = 0; i < 16; i++) {
			Random rd = new Random();
			res += chars.charAt(rd.nextInt(chars.length() - 1));
		}
		return res;
	}

	public static String getIpAddr(HttpServletRequest request) {
		String ip = request.getHeader("x-forwarded-for");
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("Proxy-Client-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("WL-Proxy-Client-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getRemoteAddr();
		}
		return ip;
	}

	@SuppressWarnings("rawtypes")
	public static String createSign(String characterEncoding, SortedMap<Object, Object> parameters) {
		StringBuffer sb = new StringBuffer();
		Set es = parameters.entrySet();
		Iterator it = es.iterator();
		while (it.hasNext()) {
			Map.Entry entry = (Map.Entry) it.next();
			String k = (String) entry.getKey();
			Object v = entry.getValue();
			if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
				sb.append(k + "=" + v + "&");
			}
		}
		System.out.println("---------------------" + sb.toString());
		String sign = MD5Encode(sb.toString(), characterEncoding).toUpperCase();
		return sign;
	}

	public static String MD5Encode(String origin, String charsetname) {
		String resultString = null;
		try {
			resultString = new String(origin);
			MessageDigest md = MessageDigest.getInstance("MD5");
			if (charsetname == null || "".equals(charsetname))
				resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
			else
				resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname)));
		} catch (Exception exception) {
		}
		return resultString;
	}

	private static String byteArrayToHexString(byte b[]) {
		StringBuffer resultSb = new StringBuffer();
		for (int i = 0; i < b.length; i++)
			resultSb.append(byteToHexString(b[i]));
		return resultSb.toString();
	}

	private static String byteToHexString(byte b) {
		int n = b;
		if (n < 0)
			n += 256;
		int d1 = n / 16;
		int d2 = n % 16;
		return hexDigits[d1] + hexDigits[d2];
	}

	private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d",
			"e", "f" };

	@SuppressWarnings("rawtypes")
	public static String getRequestXml(SortedMap<Object, Object> parameters) {
		StringBuffer sb = new StringBuffer();
		sb.append("<xml>");
		Set es = parameters.entrySet();
		Iterator it = es.iterator();
		while (it.hasNext()) {
			Map.Entry entry = (Map.Entry) it.next();
			String k = (String) entry.getKey();
			String v = (String) entry.getValue();
			if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) {
				sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");
			} else {
				sb.append("<" + k + ">" + v + "</" + k + ">");
			}
		}
		sb.append("</xml>");
		System.out.println("sb:" + sb);
		return sb.toString();
	}

	/**
	 * 发送https请求
	 * 
	 * @param requestUrl
	 *            请求地址
	 * @param requestMethod
	 *            请求方式（GET、POST）
	 * @param outputStr
	 *            提交的数据
	 * @return 返回微信服务器响应的信息
	 */
	public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) {
		try {
			// 创建SSLContext对象，并使用我们指定的信任管理器初始化
			TrustManager[] tm = { new MyX509TrustManager() };
			SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
			sslContext.init(null, tm, new java.security.SecureRandom());
			// 从上述SSLContext对象中得到SSLSocketFactory对象
			SSLSocketFactory ssf = sslContext.getSocketFactory();
			URL url = new URL(requestUrl);
			HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
			conn.setSSLSocketFactory(ssf);
			conn.setDoOutput(true);
			conn.setDoInput(true);
			conn.setUseCaches(false);
			// 设置请求方式（GET/POST）
			conn.setRequestMethod(requestMethod);
			conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");
			System.out.println("conn:" + conn);
			// 当outputStr不为null时向输出流写数据
			if (null != outputStr) {
				OutputStream outputStream = conn.getOutputStream();
				// 注意编码格式
				outputStream.write(outputStr.getBytes("UTF-8"));
				outputStream.close();
			}
			// 从输入流读取返回内容
			InputStream inputStream = conn.getInputStream();
			InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
			BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
			String str = null;
			StringBuffer buffer = new StringBuffer();
			while ((str = bufferedReader.readLine()) != null) {
				buffer.append(str);
			}
			// 释放资源
			bufferedReader.close();
			inputStreamReader.close();
			inputStream.close();
			inputStream = null;
			conn.disconnect();
			return buffer.toString();
		} catch (ConnectException ce) {
			System.out.println("连接超时");
		} catch (Exception e) {
			System.out.println("请求异常");
		}
		return null;
	}

	public static String create_nonce_str() {
		return UUID.randomUUID().toString();
	}

	public static String create_timestamp() {
		return Long.toString(System.currentTimeMillis() / 1000);
	}

	/**
	 * 解析xml
	 */
	public static Map<String, String> doXMLParse(String strxml) throws Exception {
		strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
		if (null == strxml || "".equals(strxml)) {
			return null;
		}
		Map<String, String> m = new HashMap<String, String>();
		InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
		SAXBuilder builder = new SAXBuilder();
		Document doc = builder.build(in);
		Element root = doc.getRootElement();
		List list = root.getChildren();
		Iterator it = list.iterator();
		while (it.hasNext()) {
			Element e = (Element) it.next();
			String k = e.getName();
			String v = "";
			List children = e.getChildren();
			if (children.isEmpty()) {
				v = e.getTextNormalize();
			} else {
				v = getChildrenText(children);
			}

			m.put(k, v);
		}
		// 关闭流
		in.close();
		return m;
	}

	/**
	 * 获取子结点的xml
	 */
	public static String getChildrenText(List children) {
		StringBuffer sb = new StringBuffer();
		if (!children.isEmpty()) {
			Iterator it = children.iterator();
			while (it.hasNext()) {
				Element e = (Element) it.next();
				String name = e.getName();
				String value = e.getTextNormalize();
				List list = e.getChildren();
				sb.append("<" + name + ">");
				if (!list.isEmpty()) {
					sb.append(getChildrenText(list));
				}
				sb.append(value);
				sb.append("</" + name + ">");
			}
		}
		return sb.toString();
	}

	public static String decryptData(String encryptDataB64, String sessionKeyB64, String ivB64) {
		return new String(
				decryptOfDiyIV(org.bouncycastle.util.encoders.Base64.decode(encryptDataB64),
						org.bouncycastle.util.encoders.Base64.decode(sessionKeyB64),
						org.bouncycastle.util.encoders.Base64.decode(ivB64)));
	}


	private static void init(byte[] keyBytes) {
		// 如果密钥不足16位，那么就补足. 这个if 中的内容很重要
		int base = 16;
		if (keyBytes.length % base != 0) {
			int groups = keyBytes.length / base + (keyBytes.length % base != 0 ? 1 : 0);
			byte[] temp = new byte[groups * base];
			Arrays.fill(temp, (byte) 0);
			System.arraycopy(keyBytes, 0, temp, 0, keyBytes.length);
			keyBytes = temp;
		}
		// 初始化
		Security.addProvider(new BouncyCastleProvider());
		// 转化成JAVA的密钥格式
		key = new SecretKeySpec(keyBytes, KEY_ALGORITHM);
		try {
			// 初始化cipher
			cipher = Cipher.getInstance(ALGORITHM_STR, "BC");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 解密方法
	 *
	 * @param encryptedData
	 *            要解密的字符串
	 * @param keyBytes
	 *            解密密钥
	 * @param ivs
	 *            自定义对称解密算法初始向量 iv
	 * @return 解密后的字节数组
	 */
	private static byte[] decryptOfDiyIV(byte[] encryptedData, byte[] keyBytes, byte[] ivs) {
		byte[] encryptedText = null;
		init(keyBytes);
		try {
			cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(ivs));
			encryptedText = cipher.doFinal(encryptedData);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return encryptedText;
	}
}
